package paim.wingchun.model.pojos;


import java.util.Objects;

import javax.persistence.AccessType;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.Transient;

import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.annotations.OptimisticLockType;

import paim.wingchun.app.Hibernates;
import paim.wingchun.app.Reflections;
import paim.wingchun.app.WC_Log;
import paim.wingchun.app.WC_Strings;
import paim.wingchun.model.modelos.ErroModel;
import paim.wingchun.model.modelos.ErroModel.Level;
import paim.wingchun.model.modelos.ErroModel.Tipo;

@javax.persistence.Entity(access = AccessType.FIELD)
@org.hibernate.annotations.Entity(optimisticLock = OptimisticLockType.VERSION, mutable = false)
@Inheritance(strategy = InheritanceType.JOINED)
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Erro extends Pojo {

    private static final long serialVersionUID = 1L;

    private String nome;

    private String mensagem;

    @Transient
    private Pojo pojo;

    @Transient
    private String fieldName;

    @Transient
    private String rotuloField;

    @Transient
    private Tipo tipo;

    @Transient
    private Level level = Level.ERRO;

    /** pode conter <$1>, <$2> que deverao ser substituido pelos parametros */
    @Transient
    private String rawMensagem;

    @Transient
    private String[] parametros;

    private Erro() {
        super();
    }

    public Erro(Long id, Level level, Tipo tipo, Pojo pojo, String fieldName, String[] parametros) {
        this();
        this.id = id;
        this.level = level;
        this.tipo = tipo;
        this.pojo = pojo;
        this.fieldName = fieldName;
        this.parametros = parametros;

        this.rotuloField = Reflections.getRotulo(pojo.getClass(), fieldName);

        /* estou permitindo o uso de erro com id = 0 para mensagens que nao estao em banco */
        if ( id != null && id > 0L )
            buscaBanco();

        /* adiciona esse erro ao objeto pojo */
        if ( this.pojo != null ) {
            this.pojo.getErros().add(this);
        }
    }

    public Erro(Long id, Level level, Tipo tipo) {
        this(id, level, tipo, null, null, null);
    }

    public Erro(Long id, Level level, Tipo tipo, Pojo pojo, String fieldName) {
        this(id, level, tipo, pojo, fieldName, null);
    }

    public Erro(Long id, Level level, Tipo tipo, String[] parametros) {
        this(id, level, tipo, null, null, parametros);
    }

    public Erro(Long id, Level level, Pojo pojo, String fieldName, String[] parametros) {
        this(id, level, ErroModel.Tipo.VALIDACAO, pojo, fieldName, parametros);
    }

    private void buscaBanco() {
        Erro erro;
        try {
            /* Busca nome e mensagem do erro a partir do seu codigo */
            erro = ErroModel.get().get(Hibernates.openSession(), id);
        }
        catch ( Exception e ) {
            erro = null;
            WC_Log.getInstancia().getlogger().log(java.util.logging.Level.SEVERE, e.getMessage(), e);
        }

        if ( erro == null ) {
            this.nome = "Tabela de Erros com problemas.";
            this.rawMensagem = "A tabela de erros do sistema está sem dados!\n Falta mensagem " + id + ".";
            this.mensagem = this.rawMensagem;
        }
        else {
            this.nome = erro.getNome();
            this.rawMensagem = erro.getMensagem();
        }

        if ( this.parametros != null )
            this.mensagem = WC_Strings.substParametros(this.rawMensagem, this.parametros);
        else
            this.mensagem = this.rawMensagem;

        // XXX verificar essa substituicao se realmente eh necessaria
        this.mensagem = this.mensagem.replaceAll("\\\\n", "\n");
    }

    public String getNome() {
        return nome;
    }

    public void setNome(String nome) {
        this.nome = nome;
    }

    public String getMensagem() {
        return mensagem;
    }

    public void setMensagem(String mensagem) {
        this.mensagem = mensagem;
    }

    public Level getLevel() {
        return level;
    }

    public void setLevel(Level level) {
        this.level = level;
    }

    public String getFieldName() {
        return fieldName;
    }

    public void setFieldName(String fieldName) {
        this.fieldName = fieldName;
    }

    public String getRotuloField() {
        return this.rotuloField;
    }

    public void setRotuloField(String rotuloField) {
        this.rotuloField = rotuloField;
    }

    public void setTipo(Tipo tipo) {
        this.tipo = tipo;
    }

    public Tipo getTipo() {
        return tipo;
    }

    public Pojo getPojo() {
        return this.pojo;
    }

    public void setPojo(Pojo pojo) {
        this.pojo = pojo;
    }

    public String[] getParametros() {
        return parametros;
    }

    public void setParametros(String[] parValue) {
        this.parametros = parValue;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = super.hashCode();
        result = prime * result + ((this.pojo == null) ? 0 : this.pojo.hashCode());
        result = prime * result + ((this.fieldName == null) ? 0 : this.fieldName.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if ( !super.equals(obj) )
            return false;

        final Erro other = (Erro) obj;

        if ( !Objects.equals(this.fieldName, other.fieldName) )
            return false;

        if ( !equals(this.pojo, other.pojo) )
            return false;

        return true;
    }

    @Override
    public String toString() {
        String s = super.toString();
        s += "[" + this.getNome();
        s += "]";
        return s;
    }

}
